package xyz.hzxiao.utils;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Arrays;

/**
 * sm4加解密工具类
 *
 * @author ：FuXx
 * @className : com.usedcar.utils.Sm4Utils
 * @since ：2023-08-18
 */
public class Sm4Utils {

    private static final Logger log = LoggerFactory.getLogger(Sm4Utils.class);

    static {
        // 初始化安全性提供者
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * 自定义32位16进制密钥
     */
    private static final String KEY = "86A66236C5201FD8F87C869FA606845B";

    /**
     * 编码集
     */
    private static final String ENCODING = "UTF-8";

    /**
     * 算法名称
     */
    public static final String ALGORITHM_NAME = "SM4";

    /**
     * 加密算法/分组加密模式/分组填充方式
     * PKCS5Padding-以8个字节为一组进行分组加密
     * 定义分组加密模式使用：PKCS5Padding
     */
    public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding";

    /**
     * 默认密钥的位数: 128-->32位16进制；256-->64位16进制
     */
    public static final int DEFAULT_KEY_SIZE = 128;

    /**
     * 功能描述:
     * 生成ECB暗号
     *
     * @param mode 模式
     * @param key
     * @throws Exception
     * @explain ECB模式（电子密码本模式：Electronic codebook）
     * @Return: javax.crypto.Cipher
     * @Author: FuX
     * @Update:
     * @Date: 13:50
     */
    private static Cipher generateEcbCipher(int mode, byte[] key) throws Exception {
        Cipher cipher = Cipher.getInstance(Sm4Utils.ALGORITHM_NAME_ECB_PADDING, BouncyCastleProvider.PROVIDER_NAME);
        Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
        cipher.init(mode, sm4Key);
        return cipher;
    }

    /**
     * 功能描述:
     * 获取自动生成密钥
     *
     * @Return: byte[] 密钥字节数组
     * @Author: FuX
     * @Update:
     * @Date: 13:52
     */
    public static byte[] getAutoKey() throws Exception {
        return generateKey(DEFAULT_KEY_SIZE);
    }

    /**
     * 功能描述:
     * 自动生成密钥
     *
     * @param keySize 密钥的位数
     * @Return: byte[]
     * @Author: FuX
     * @Update:
     * @Date: 13:52
     */
    public static byte[] generateKey(int keySize) throws Exception {
        KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME);
        kg.init(keySize, new SecureRandom());
        return kg.generateKey().getEncoded();
    }

    /**
     * 功能描述:
     * 自定义16进制密钥加密
     *
     * @param paramStr 待加密字符串
     * @Return: java.lang.String 返回16进制的加密字符串
     * @Author: FuX
     * @Update:
     * @Date: 13:54
     * @explain 加密模式：ECB
     * 密文长度不固定，会随着被加密字符串长度的变化而变化
     */
    public static String encryptEcb(String paramStr) throws Exception {
        // 16进制字符串-->byte[]
        byte[] keyData = ByteUtils.fromHexString(KEY);
        // String-->byte[]
        byte[] srcData = paramStr.getBytes(ENCODING);
        // 加密后的数组
        byte[] cipherArray = encryptEcbPadding(keyData, srcData);
        // byte[]-->hexString
        return ByteUtils.toHexString(cipherArray);
    }

    /**
     * 功能描述:
     * 加密模式之Ecb
     *
     * @param key  加密16进制密钥
     * @param data 加密内容
     * @Return: byte[]
     * @Author: FuX
     * @Update:
     * @Date: 13:55
     */
    public static byte[] encryptEcbPadding(byte[] key, byte[] data) throws Exception {
        Cipher cipher = generateEcbCipher(Cipher.ENCRYPT_MODE, key);
        return cipher.doFinal(data);
    }

    /**
     * 功能描述:
     * sm4解密
     *
     * @param cipherText 16进制的加密字符串（忽略大小写）
     * @Return: java.lang.String 解密后的字符串
     * @Author: FuX
     * @Update:
     * @Date: 13:55
     * @explain 解密模式：采用ECB
     */
    public static String decryptEcb(String cipherText) throws Exception {
        // hexString-->byte[]
        byte[] keyData = ByteUtils.fromHexString(KEY);
        // hexString-->byte[]
        byte[] cipherData = ByteUtils.fromHexString(cipherText);
        // 解密
        byte[] srcData = decryptEcbPadding(keyData, cipherData);
        // byte[]-->String
        return new String(srcData, ENCODING);
    }

    /**
     * 功能描述:
     * 解密
     *
     * @param key        加密时密钥
     * @param cipherText 需解密的内容
     * @Return: byte[] 解密结果
     * @Author: FuX
     * @Update:
     * @Date: 13:56
     */
    public static byte[] decryptEcbPadding(byte[] key, byte[] cipherText) throws Exception {
        Cipher cipher = generateEcbCipher(Cipher.DECRYPT_MODE, key);
        return cipher.doFinal(cipherText);
    }

    /**
     * 功能描述:
     * 校验加密前后的字符串是否为同一数据
     *
     * @param cipherText 16进制加密后的字符串
     * @param paramStr   加密前的字符串
     * @Return: boolean 是否为同一数据
     * @Author: FuX
     * @Update:
     * @Date: 13:57
     */
    public static boolean verifyEcb(String cipherText, String paramStr) throws Exception {
        // 密钥转成字节数组
        byte[] keyData = ByteUtils.fromHexString(KEY);
        // 将16进制字符串转换成数组
        byte[] cipherData = ByteUtils.fromHexString(cipherText);
        // 解密
        byte[] decryptData = decryptEcbPadding(keyData, cipherData);
        // 将原字符串转换成byte[]
        byte[] srcData = paramStr.getBytes(ENCODING);
        // 判断2个数组是否一致
        return Arrays.equals(decryptData, srcData);
    }

    public static void main(String[] args) {
//        String throbbingName = throbbingName();
//        System.out.println(throbbingName);
        encrypt();
    }

    /**
     * 功能描述:
     *
     * @param
     * @Return: java.lang.String girlFriendName
     * @Author: FuX
     * @explain:
     * @Update:
     * @Date: 2020-12-19
     */
    private static String throbbingName() {
        char[] girlFriend = new char[3];
        girlFriend[0] = '\u5218';
        girlFriend[1] = '\u98de';
        girlFriend[2] = '\u7ea2';
        return new String(girlFriend);
    }

    private static void encrypt() {
        try {
            // 加密内容
            // String text = "只要朝着一个方向努力,一切都会变的得心应手";
            // String text = "17606875329";
            String text = "test1@baomidou.com";
            // 加密字符串
            String ecb = Sm4Utils.encryptEcb(text);
            log.info("加密的字符串:{}", ecb);
            // 验证加密前内容和加密后内容是否相等r
            log.info("加密前与加密后: {}", Sm4Utils.verifyEcb(ecb, text));
            // 解密
            String decryptEcb = Sm4Utils.decryptEcb(ecb);
            log.info("解密字符串: {}", decryptEcb);
        } catch (Exception ex) {
            log.error("加密错误", ex);
        }
    }
}
